package com.rdtravel.web;

import com.rdtravel.entities.Booking;
import com.rdtravel.entities.Trip;
import com.rdtravel.exceptions.AnotherCustomerBookedTheSameTripAtTheSameMomentException;
import com.rdtravel.exceptions.NotEnoughAvaiableSeatsException;
import com.rdtravel.services.BookingsService;
import com.rdtravel.services.CustomersService;
import com.rdtravel.valueobjects.CreditCard;
import com.rdtravel.valueobjects.WayOfPayment;

import java.security.Principal;
import java.text.NumberFormat;
import java.util.Locale;

import javax.validation.Valid;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.MessageSource;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;

@Controller
@RequestMapping("/bookings")
class BookingsController {
	private final static String CREATE_VIEW = "bookings/create";
	private final static String REDIRECT_AFTER_BOOKING_CREATED = "redirect:/bookings/{id}";
	private final static String BOOKING_VIEW = "/bookings/booking";
	private final CustomersService customersService;
	private final BookingsService bookingsService;
	private final MessageSource messageSource;

	@Autowired
	public BookingsController(CustomersService customersService,
			BookingsService bookingsService, MessageSource messageSource) {
		this.customersService = customersService;
		this.bookingsService = bookingsService;
		this.messageSource = messageSource;
	}

	@RequestMapping(value = "create", method = RequestMethod.GET, params = "trip")
	public ModelAndView createForm(@RequestParam(required = false) Trip trip, @RequestParam(defaultValue="1") int seats, 
			Principal principal) {
		if (trip == null) {
			return new ModelAndView("redirect:/");
		}
		Booking booking = new Booking();
		booking.setTrip(trip);
		booking.setSeats(seats);
		booking.setCustomer(customersService.findByUsername(principal.getName()));
		bookingsService.setPriceAndDiscounts(booking);
		ModelAndView modelAndView = new ModelAndView(CREATE_VIEW);
		modelAndView.addObject(booking);
		fillModelWithExpirationMonthsAndYears(modelAndView);
		return modelAndView;
	}

	@RequestMapping(method = RequestMethod.POST)
	public ModelAndView create(@Valid Booking booking,
			BindingResult bindingResult, Principal principal) {
		booking.setCustomer(customersService.findByUsername(principal.getName()));
		bookingsService.setPriceAndDiscounts(booking);
		if (!bindingResult.hasErrors()) {
			try {
				bookingsService.create(booking);
				ModelAndView modelAndView = new ModelAndView(REDIRECT_AFTER_BOOKING_CREATED);
				modelAndView.addObject("id", booking.getId());
				modelAndView.addObject("created", true);
				return modelAndView;
			} catch (NotEnoughAvaiableSeatsException ex) {
				bindingResult.rejectValue("seats",
						"seatsShouldBeLessOrEqualThanAvailableSeats");
			} catch (AnotherCustomerBookedTheSameTripAtTheSameMomentException ex2) {
				bindingResult.reject("tooManyConcurrentCustomers");
			}
		}
		ModelAndView modelAndView = new ModelAndView(CREATE_VIEW);
		fillModelWithExpirationMonthsAndYears(modelAndView);
		return modelAndView;
	}

	private void fillModelWithExpirationMonthsAndYears(ModelAndView modelAndView) {
		modelAndView.addObject("expirationMonthNumbers",
				CreditCard.getExpirationMonthNumbers());
		modelAndView.addObject("expirationYearNumbers",
				CreditCard.getExpirationYearNumbers());
	}

	@RequestMapping(value = "{booking:\\d+}", method = RequestMethod.GET)
	public ModelAndView findById(@PathVariable Booking booking) {
		return new ModelAndView(BOOKING_VIEW, "booking", booking);
	}

	@RequestMapping(value = "price", method = RequestMethod.GET, params = {
			"trip", "seats", "creditcard" }, produces = MediaType.APPLICATION_JSON_VALUE)
	public @ResponseBody
	String[] getPrice(@RequestParam Trip trip, @RequestParam int seats,
			@RequestParam boolean creditcard, Locale locale) {
		Booking booking = new Booking();
		booking.setTrip(trip);
		booking.setSeats(seats);
		if (creditcard) {
			booking.setWayOfPayment(WayOfPayment.CREDITCARD);
		}
		bookingsService.setPriceAndDiscounts(booking);
		NumberFormat numberFormat=NumberFormat.getInstance(locale);
		String[] result = {
				messageSource.getMessage("price", new Object[] { seats, numberFormat.format(booking.getGrossPricePerPerson()), numberFormat.format(booking.getGrossPrice()) }, locale),
				messageSource.getMessage("price", new Object[] { seats, numberFormat.format(booking.getNetPricePerPerson()), numberFormat.format(booking.getNetPrice()) }, locale) };
		return result;
	}
}
